package net.chowda.castcluster.util;

import org.apache.log4j.Logger;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * this class provides some utility methods to wrap ffmpeg access.
 */
public class MovieUtil {
    private static final Logger LOG = LogUtil.getLogger(MovieUtil.class);

    public static void convertVideoToMPEG4(String sourceFile, String destFile) {
        LOG.info("converting " + sourceFile + " to " + destFile);
        LOG.info("this might take a while... and there won't be much output.");

        List<String> cmds = new ArrayList<String>();

        cmds.addAll(Arrays.asList(getFFMPEGPath(), "-v", "0"));
        cmds.add("-i");
        cmds.add(sourceFile);

        // see if we want to use something other than the default bitrates.
        String vidBR = getVideoBitrate(sourceFile);
        if(vidBR != null) {
            cmds.add("-b");
            cmds.add(vidBR);
        }
        String audBR = getAudioBitrate(sourceFile);
        if(audBR != null) {
            cmds.add("-ab");
            cmds.add(audBR);
        }

        cmds.add(destFile);

        // execute command, write output to stdout.
        int exitCode = runCmd(cmds.toArray(new String[]{}), System.out);

        if(exitCode != 0) {
            // clean up the bad file.
            File tmp = new File(destFile);
            if(tmp.exists()) tmp.delete();

            // we were unable to convert... we'll try it again with a guess at the sampling rate.
            LOG.info("we couldn't do the conversion with out a sampling rate, we're going to try it again with a reasonable default.");
            cmds = new ArrayList<String>();

            cmds.addAll(Arrays.asList(getFFMPEGPath(), "-v", "0"));

            cmds.add("-i");
            cmds.add(sourceFile);
            cmds.add("-ar");
            cmds.add("44100");

            // see if we want to use something other than the default bitrates.
            vidBR = getVideoBitrate(sourceFile);
            if(vidBR != null) {
                cmds.add("-b");
                cmds.add(vidBR);
            }

            audBR = getAudioBitrate(sourceFile);
            if(audBR != null) {
                cmds.add("-ab");
                cmds.add(audBR);
            }

            cmds.add(destFile);

            // execute command, write output to stdout.
            exitCode = runCmd(cmds.toArray(new String[]{}), System.out);
        }
        LOG.info("conversion complete. exit code: " + exitCode);
    }

    /**
     * parses video bitrate from ffmpeg output
     *
     * if we just pass -i <filename> to ffmpeg, we get details about the file... 
     */
    public static String getVideoBitrate(String videoPath) {
        StringWriter stringWriter = new StringWriter();
        runCmd(new String[] {getFFMPEGPath(), "-i", videoPath}, new WriterOutputStream(stringWriter));

        Pattern pattern = Pattern.compile("bitrate:\\s(\\d*)\\skb", Pattern.MULTILINE);
        String output = stringWriter.getBuffer().toString();
        LOG.info(output);
        Matcher matcher = pattern.matcher(output);
        String bitrate = null;
        if(matcher.find()) bitrate = matcher.group(1);
        return bitrate;
    }

    public static String getVideoType(String videoPath) {
        StringWriter stringWriter = new StringWriter();
        runCmd(new String[] {getFFMPEGPath(), "-i", videoPath}, new WriterOutputStream(stringWriter));

        Pattern pattern = Pattern.compile("Video:\\s(.*?),", Pattern.MULTILINE);
        String output = stringWriter.getBuffer().toString();
        LOG.info(output);
        Matcher matcher = pattern.matcher(output);
        String bitrate = null;
        if(matcher.find()) bitrate = matcher.group(1);
        return bitrate;
    }

    /**
     * parses audio bitrate from ffmpeg output
     */
    public static String getAudioBitrate(String videoPath) {
        StringWriter stringWriter = new StringWriter();
        runCmd(new String[] {getFFMPEGPath(), "-i", videoPath}, new WriterOutputStream(stringWriter));
        Pattern pattern = Pattern.compile("Audio:.*?\\s(\\d*)\\skb", Pattern.MULTILINE);
        Matcher matcher = pattern.matcher(stringWriter.getBuffer().toString());
        String bitrate = null;
        if(matcher.find()) bitrate = matcher.group(1);
        return bitrate;
    }

    private static int runCmd(String[] cmdarray, OutputStream out) {
        LOG.info("using command array: " + Arrays.asList(cmdarray));
        int exitCode;
        try {
            Process process = new ProcessBuilder(cmdarray).redirectErrorStream(true).start();

            InputStream inputStream = process.getInputStream();
            byte[] b = new byte[2048];
            int count;
            while((count = inputStream.read(b)) > -1) { out.write(b,0,count); }
            out.flush();
            exitCode = process.waitFor();
            LOG.info("process exited with code: " + exitCode);
        } catch (IOException e) {
            LOG.error("could not run command!", e);
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            LOG.error("could not run command!", e);
            throw new RuntimeException(e);
        }
        return exitCode;
    }

    private static String getFFMPEGPath() {
        return CastClusterConfig.getInstance().getGlobalProp(CastClusterConfig.CONVERTER_PATH);
    }
}
